#include "SceneNode.h"

namespace Nezha
{

	SceneNode::SceneNode(const _string& name)
		:ControlledObject(name)
		,mSceneLayer(RL_DEFAULT)
	{
			
	}

	SceneNode::~SceneNode()
	{
		// TODO
	}

	void SceneNode::setPosition(const Vector3& pos)
	{
		mPosition = pos;
		NZSetBitTrue(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM | SOU_WORLDTRANSFORM_MATRIX | SOU_CHILDREN);
	}

	void SceneNode::setScale(const Vector3& scale)
	{
		NZ_ASSERT(scale.x != 0 && scale.y != 0 && scale.z != 0);

		mScale = scale;
		NZSetBitTrue(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM | SOU_WORLDTRANSFORM_MATRIX | SOU_CHILDREN);
	}

	void SceneNode::setOrientation(const Quaternion& q)
	{
		mOrientation = q;
		mOrientation.normalize();
		NZSetBitTrue(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM | SOU_WORLDTRANSFORM_MATRIX | SOU_CHILDREN);
	}

	void SceneNode::translate(const Vector3& trans, bool localSpace /* = true */)
	{
		if(localSpace)
		{
			mPosition += mOrientation * trans;
		}
		else
		{
			if(getParentSceneNode())
			{
				mPosition += (getParentSceneNode()->getWorldOrientation().inverse() * trans) / mScale;
			}
			else
			{
				mPosition += trans;
			}
		}

		NZSetBitTrue(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM | SOU_WORLDTRANSFORM_MATRIX | SOU_CHILDREN);
	}

	void SceneNode::rotate(const Matrix3x3& rotMat, bool localSpace /* = true */)
	{
		Quaternion q;
		q.fromRotation(rotMat);
		rotate(q, localSpace);
	}

	void SceneNode::rotate(const Quaternion& q, bool localSpace /* = true */)
	{
		Quaternion p = q;
		p.normalize();

		if(localSpace)
		{
			mOrientation = mOrientation * p;
		}
		else
		{
			/**
			remarks concatenate sequence !!
				q * world = parent * L <=> world = parent * L0
				q * world = (world * L0^-1) * L
				L = (L0 * world^-1) * q * world
			*/
			mOrientation = mOrientation * getWorldOrientation().inverse() * p * getWorldOrientation();
		}

		NZSetBitTrue(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM | SOU_WORLDTRANSFORM_MATRIX | SOU_CHILDREN);
	}

	const Matrix4x4& SceneNode::getWorldTransform() const
	{
		if(NZTestFlag(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM_MATRIX))
		{
			Matrix3x3 rotMat;
			mWorldOrientation.toRotation(rotMat);
			rotMat.timesDiagonal(mWorldScale);
			mHMatrixL2W.setRow(0, rotMat[0][0], rotMat[0][1], rotMat[0][2], mWorldPos.x);
			mHMatrixL2W.setRow(1, rotMat[1][0], rotMat[1][1], rotMat[1][2], mWorldPos.x);
			mHMatrixL2W.setRow(2, rotMat[2][0], rotMat[2][1], rotMat[2][2], mWorldPos.x);
			mHMatrixL2W.setRow(3, 0, 0, 0, 1.0f);

			NZSetBitFalse(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM_MATRIX);
		}

		return mHMatrixL2W;
	}

	void SceneNode::updateTransform(float dt, u32 flag /* = TransformUpdateBits */)
	{
		mTransformNBoundUpdateFlag |= flag;

		if(NZTestFlag(mTransformNBoundUpdateFlag, SOU_CONTROLLER))
		{
			// generally, for animation.
			updateController(dt);
			NZSetBitFalse(mTransformNBoundUpdateFlag, SOU_CONTROLLER);
		}

		_updateTransform(dt);
	}

	void SceneNode::updateBound(u32 flag /* = BoundUpdateBits */)
	{
		mTransformNBoundUpdateFlag |= flag;

		if(NZTestFlag(mTransformNBoundUpdateFlag, SOU_BOUND))
		{
			_updateBound();
			NZSetBitFalse(mTransformNBoundUpdateFlag, SOU_BOUND);
		}

		if(NZTestFlag(mTransformNBoundUpdateFlag, SOU_BOUND_UPWARD))
		{
			updateBoundUpward();
			NZSetBitFalse(mTransformNBoundUpdateFlag, SOU_BOUND_UPWARD);
		}
	}

	void SceneNode::_updateBound()
	{
		// do nothing here.
	}

	void SceneNode::updateBoundUpward()
	{
		if(getParentSceneNode())
		{
			getParentSceneNode()->updateBound(SOU_BOUND | SOU_BOUND_UPWARD);
			getParentSceneNode()->updateBoundUpward();
		}
	}

	void SceneNode::_updateTransform(float dt)
	{
		if(NZTestFlag(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM))
		{
			if(getParentSceneNode())
			{
				SceneNode* parentNode = getParentSceneNode();
				const Quaternion& p = parentNode->getWorldOrientation();
				mWorldOrientation = p * mOrientation;

				const Vector3& ps = parentNode->getWorldSale();
				mWorldScale = mScale * ps;

				mWorldPos = p * (ps * mPosition) + parentNode->getWorldPos();
			}
			else
			{
				mWorldOrientation = mOrientation;
				mWorldPos = mPosition;
				mWorldScale = mScale;
			}

			NZSetBitFalse(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM);
			NZSetBitTrue(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM_MATRIX);
		}

		if(NZTestFlag(mTransformNBoundUpdateFlag, SOU_CHILDREN))
		{
			for(PArray<SceneNode*>::iterator it = mChildren.begin(); it != mChildren.end(); it++)
			{
				if((*it))
				{
					(*it)->updateTransform(dt, SOU_WORLDTRANSFORM | SOU_CHILDREN);
				}
			}
		}
	}

	void SceneNode::attachChild(SceneNode* child)
	{
		NZ_ASSERT(child);
		NZ_ASSERT(NULL == child->getParentSceneNode());

		PArray<SceneNode*>::iterator it = find(mChildren.begin(), mChildren.end(), child);
		if(it != mChildren.end())
		{
			NZWarning("scene node %s already attached to node %s", child->getName().c_str(), mName.c_str());
			return;
		}

		if(preAttachChild(child))
		{
			mChildren.push_back(child);

			onAttachChild(child);
		}
	}

	SceneNode* SceneNode::getChild(u32 i)
	{
		if(i < mChildren.size())
		{
			return mChildren[i];
		}

		NZWarning("Invliad index %d for getChild, node %s", i, mName.c_str());
		return NULL;
	}

	SceneNode* SceneNode::getChild(const _string& name)
	{
		for(PArray<SceneNode*>::iterator it = mChildren.begin(); it != mChildren.end(); it++)
		{
			if((*it)->getName() == name)
			{
				return (*it);
			}
		}

		NZWarning("Invliad name %d for getChild, node %s", name.c_str(), mName.c_str());
		return NULL;
	}

	void SceneNode::removeChild(SceneNode* child)
	{
		NZ_ASSERT(child);

		PArray<SceneNode*>::iterator it = find(mChildren.begin(), mChildren.end(), child);
		if(it != mChildren.end())
		{			
			mChildren.erase(it);
			onRemoveChild(child);
		}
	}

	void SceneNode::removeChild(const _string& name)
	{
		for(PArray<SceneNode*>::iterator it = mChildren.begin(); it != mChildren.end(); it++)
		{
			if((*it)->getName() == name)
			{
				mChildren.erase(it);
				onRemoveChild((*it));
				return;
			}
		}
	}

	void SceneNode::removeAllChild()
	{
		for(PArray<SceneNode*>::iterator it = mChildren.begin(); it != mChildren.end(); it++)
		{
			onRemoveChild((*it));
		}

		mChildren.clear();
	}

	bool SceneNode::preAttachChild(SceneNode* t)
	{
		// TODO

		return true;
	}

	void SceneNode::onAttachChild(SceneNode* t)
	{
		// TODO
	}

	void SceneNode::onRemoveChild(SceneNode* t)
	{
		// TODO
	}

	void SceneNode::bindOject(SceneObject* obj)
	{
		if(mBindedObject != obj)
		{
			if(obj)
			{
				NZ_ASSERT(!mBindedObject->getSceneNode());
				mBindedObject = obj;
				mBindedObject->bind2SceneNode(this);
			}
			else
			{
				if(mBindedObject.get())
				{
					mBindedObject->bind2SceneNode(NULL);
				}

				mBindedObject = NULL;
			}
		}
	}

}//end namespace Nezha